package br.edu.ufcg.splab.techniques.selection;

import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.techniques.reduction.functions.similarity.DistanceFunction;
import br.edu.ufcg.splab.techniques.reduction.strategies.SimilarityMatrix;

public class SelectionSimilarity implements InterfaceSelection {
	
	private DistanceFunction function;
	
	/**
	 * testSuite is a list with the test cases
	 */
	private TestSuite testSuite;	
	
	/**
	 * listRemove is a list with the test cases removed 
	 */
	private TestSuite listRemove;
	
	/**
	 * 
	 * matrizOfSimilarity is the matrix with the percentage 
	 * of similarity between two test cases, where line is one test case and  
	 * column is another test case  
	 */
	private double[][] matrixOfSimilaridade;
	
	private SimilarityMatrix similarityMatrix;
	/**
	 * The constructor of a Selection Similarity Technique.
	 * The test suite will be generated in this constructor. 
	 * @param function The similarity function of the test cases.
	 */
	public SelectionSimilarity(DistanceFunction function)	{
		this.function = function;
		this.similarityMatrix = new SimilarityMatrix();
	}
	
	/**
	 * Define the subset of the test suite from a percentage.   
	 * @param suite The test suite original.
	 * @param percent is the percentage of test cases that a testSuite must have after
	 * @return The reduced test suite.
	 */
	public TestSuite selectTestSuite(TestSuite testSuite, double percent){
		this.matrixOfSimilaridade = similarityMatrix.createMatrix(testSuite, function);		
		removeNtestsSimilar(percent);
		for(TestCase t : listRemove){
			if(this.testSuite.contains(t)){
				this.testSuite.remove(t);
			}
		}		
		return this.testSuite;		
	}
	
	/*
	 * In this algorithms removed (1-percent) of test cases	 
	 * @param percent
	 */
	private void removeNtestsSimilar(double percent) {
		int numberEliminate = (int) (this.matrixOfSimilaridade.length * percent);
		for (int i = numberEliminate; i > 0; i--) {
			removeTestsSimilar();
		}
	}
	
	/*
	 * Eliminates the row and column of the matrix,
	 * fills with less line and column
	 */
	//preenche com menos a linha e a coluna referente ao teste eliminado
	private void fillsLess(int linha) {
		for (int i = 0; i < this.matrixOfSimilaridade[linha].length; i++) {
			this.matrixOfSimilaridade[linha][i] = -1;
			this.matrixOfSimilaridade[i][linha] = -1;
		}
	}
	
	/*
	 * Responsible for returning the position of greatest similarity of matrix
	 * @return (i,j) cordinates of matrix
	 */
	//me retorna a posicao (i,j) dos casos com maior similaridade
	private double[] getPositionHigher() {
		int line = 0;
		int colunm = 0;
		double bigger = -99;
		for (int i = 0; i < this.matrixOfSimilaridade.length; i++) {
			for (int j = i + 1; j < this.matrixOfSimilaridade[i].length; j++) {
				if (this.matrixOfSimilaridade[i][j] > bigger) {
					bigger = this.matrixOfSimilaridade[i][j];
					line = i;
					colunm = j;
				}
			}
		}
		double[] cordinates = { line, colunm, bigger };
		return cordinates;

	}

	/*
	 * Eliminates the lowest test case, if they have 
	 * the same size it will be done randomly
	 */
	//remove testes similares, sempre eliminamos o menor caso de teste
	//se forem de iguais tamanhos sera selecionado randomicamente
	private void removeTestsSimilar() {
		double[] cordinates = getPositionHigher();
		int lengthLine = this.testSuite.get((int) cordinates[0]).size();
		int lengthColunm = this.testSuite.get((int) cordinates[1]).size();
		
		if (lengthLine == lengthColunm) {
			int t = (int) (Math.random() * 2);
			fillsLess((int) cordinates[t]);			
			this.listRemove.add(this.testSuite.get((int)cordinates[t]));			
		} else if (lengthLine > lengthColunm) {// deleta Tcoluna
			fillsLess((int) cordinates[1]);			
			this.listRemove.add(this.testSuite.get((int)cordinates[1]));
		} else if (lengthColunm > lengthLine) {// deleta Tlinha
			fillsLess((int) cordinates[0]);			
			this.listRemove.add(this.testSuite.get((int)cordinates[0]));
		}
	}
	
}